package ss.pku.jsa.type.inference;

import java.util.List;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.TagElement;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclaration;

@SuppressWarnings("unchecked")
public class FieldSensitiveVisitor extends ASTVisitor {
	protected CompilationUnit compilationUnit = null;
	protected String fileName = null;
	Analysis analysis = null;

	public FieldSensitiveVisitor(CompilationUnit compilationUnit,
			String fileName, Analysis analysis) {
		this.compilationUnit = compilationUnit;
		this.fileName = fileName;
		this.analysis = analysis;
	}

	public boolean visit(TypeDeclaration node) {
		if (!JqualPlugin.obeyFSAnnotations)
			return true;

		Javadoc doc = node.getJavadoc();
		ITypeBinding b = node.resolveBinding();

		if (doc == null) {
			return true;
		}

		for (TagElement t : (List<TagElement>) doc.tags()) {
			String name = t.getTagName();
			if (name == null) {
				continue;
			}
			if (!name.equals("@FieldSensitive"))
				continue;
			JqualPlugin.analysis.fieldSensitiveTypes.add(TypeKey.getTypeKey(b));
			break;
		}

		return true;
	}

	public boolean visit(FieldDeclaration node) {
		Javadoc doc = node.getJavadoc();
		if (doc == null)
			return true;
		Location l = getLocation(node);

		for (TagElement el : (List<TagElement>) doc.tags()) {
			String name = el.getTagName();
			if (name == null)
				continue;
			if (name.trim().equals("@FieldSensitive")) {
				for (VariableDeclaration d : (List<VariableDeclaration>) node
						.fragments()) {
					IVariableBinding b = d.resolveBinding();

					if (b == null) {
						this.analysis
								.logMissingClass(d.getName().toString(), l);
						return true;
					}

					Profile.logMemStart("visit(FieldDeclaration)/new FieldKey");
					FieldKey fieldKey = FieldKey.getFieldKey(b);
					Profile.logMemEnd();

					if (fieldKey == null) {
						throw new IllegalArgumentException(
								"@FieldSensitive applied to local var " + node
										+ " at " + l);
					}
					this.analysis.fieldSensitiveFields.add(fieldKey);
					JqualPlugin.printlnLog("javadocLog", "added " + fieldKey
							+ " to FS");
				}
			}
		}

		return true;
	}

	private Location getLocation(ASTNode node) {
		return Location.getLocation(node, this.compilationUnit, this.fileName);
	}
}
